package com.qire.antscompiler.generator;

import com.qire.antscompiler.utils.Log;
import com.qire.antscore.annotation.DalExceptionObserve;
import com.qire.antscore.common.AssertUtils;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

/**
 * DataWarehouse 数据访问异常观察者绑定器代码生成器
 */
public class DalExceptionObserveCodeGenerator {

    // ExceptionObservable类全名
    private static final String EXCEPTION_OBSERVABLE = "com.qire.antsbinder.dal.exception.ExceptionObservable";
    // todo 可以不用接口约束，改成静态方法调用，约定好方法名即可
    // 绑定器接口全名
    private static final String I_OBSERVE_BINDER = "com.qire.antsbinder.dal.exception.IObserveBinder";
    // 绑定器接口注册方法名
    private static final String METHOD_REGISTER = "register";
    // 绑定器接口取消注册方法名
    private static final String METHOD_UNREGISTER = "unregister";
    // 类名后缀
    private static final String CLASS_NAME_SUFFIX = "DalExceptionObserveBinder";

    private Log log;

    // 节点工具类 (类、函数、属性都是节点)
    private Elements elementUtils;

    // 文件生成器 类/资源
    private Filer filerUtils;

    /**
     * 记录所有需要注入的属性 key:类节点 value:需要注入的属性节点集合
     */
    private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>();

    public DalExceptionObserveCodeGenerator(ProcessingEnvironment processingEnv){
        this.log = Log.newLog(processingEnv.getMessager());
        this.elementUtils = processingEnv.getElementUtils();
        this.filerUtils = processingEnv.getFiler();
    }

    /**
     * 记录需要生成的类与属性
     *
     * @param elements
     * @throws IllegalAccessException
     */
    private void categories(Set<? extends Element> elements) {
        for (Element element : elements) {
            //获得父节点 (类)
            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
            if (parentAndChild.containsKey(enclosingElement)) {
                parentAndChild.get(enclosingElement).add(element);
            } else {
                List<Element> childList = new ArrayList<>();
                childList.add(element);
                parentAndChild.put(enclosingElement, childList);
            }
        }
    }

    /**
     * 解析观察者绑定器
     */
    public void parseDalExceptionObserve(Set<? extends Element> elements) {

        if(AssertUtils.isEmpty(elements)) {
            log.i("DalExceptionObserveCodeGenerator：        elements 为空没有需要处理的");
            return;
        }
        log.i("DalExceptionObserveCodeGenerator：        开始处理了！");

        // 更具归属类分类
        categories(elements);

        if(AssertUtils.isEmpty(parentAndChild)) {
            return;
        }

        // 获得ViewModel类型
        TypeElement ExceptionObservable = elementUtils.getTypeElement(EXCEPTION_OBSERVABLE);
        // todo 可以不用接口约束，改成静态方法调用，约定好方法名即可
        // 获得观察者绑定器结构类型
        TypeElement IObserveBinder = elementUtils.getTypeElement(I_OBSERVE_BINDER);

        for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {

            List<Element> observeActionElementList = entry.getValue();
            if(AssertUtils.isEmpty(observeActionElementList)) {
                continue;
            }

            // 获得观察者行为所属对象真是类型
            TypeElement observeClassElement = entry.getKey();
            // 转化为ClassName
            ClassName observeClassName = ClassName.get(observeClassElement);

            // 申明一个变量 Object observe
            ParameterSpec observeSpec = ParameterSpec.builder(observeClassName, "observe").build();

            // todo 可以不用接口约束，改成静态方法调用，约定好方法名即可
            // 定义一个方法 register(T observe)
            MethodSpec.Builder registerMethodBuilder = MethodSpec.methodBuilder(METHOD_REGISTER)
                    .addAnnotation(Override.class)
                    .addModifiers(Modifier.PUBLIC)
//                    .addModifiers(Modifier.STATIC)
                    .addParameter(observeSpec);

            // todo 可以不用接口约束，改成静态方法调用，约定好方法名即可
            // 定义一个方法 unregister()
            MethodSpec.Builder unregisterMethodBuilder = MethodSpec.methodBuilder(METHOD_UNREGISTER)
                    .addAnnotation(Override.class)
                    .addModifiers(Modifier.PUBLIC)
//                    .addModifiers(Modifier.STATIC)
                    ;

            // 为方法 public void register(T observe) {} 添加函数体
            // 为方法 public void unregister() {} 添加函数体
            for(Element observeActionElement : observeActionElementList) {
                // 验证节点是否含有属性绑定注解
                DalExceptionObserve dalExceptionObserve = observeActionElement.getAnnotation(DalExceptionObserve.class);
                if(dalExceptionObserve == null) {
                    continue;
                }

                for(String tag : dalExceptionObserve.tags()){
//                ExceptionObservable.register("http://test1",observe::thirdPartyLoginExceptionHandle);
                    registerMethodBuilder.addStatement("$T.register(\"$L\",observe::$L)",
                            ClassName.get(ExceptionObservable),
                            tag,
                            observeActionElement.getSimpleName());
//                ExceptionObservable.unregister("http://test1");
                    unregisterMethodBuilder.addStatement("ExceptionObservable.unregister(\"$L\")",tag);
                }

            }

            // 生成java类名
            String newObserveClassName = observeClassElement.getSimpleName() + CLASS_NAME_SUFFIX;

            // 构建一个IObserveBinder<T>类型
            ParameterizedTypeName superinterfaceTypeName = ParameterizedTypeName.get(
                    ClassName.get(IObserveBinder),
                    observeClassName);

            // 构建一个 public class ****DalExceptionObserveBinder implements IObserveBinder{} 类
            TypeSpec DalExceptionObserveBinderClass = TypeSpec.classBuilder(newObserveClassName)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(registerMethodBuilder.build())
                    .addMethod(unregisterMethodBuilder.build())
                    .addSuperinterface(superinterfaceTypeName)
                    .build();

            //将****DalExceptionObserveBinder文件写入磁盘中,路径是在app/build/source/api/debug/下面
            try {
                JavaFile.builder(observeClassName.packageName(), DalExceptionObserveBinderClass).build().writeTo(filerUtils);
            } catch (IOException e) {
                e.printStackTrace();
            }

            log.i("DalExceptionObserveCodeGenerator 构建成功: " + observeClassName.packageName() + "." + newObserveClassName);
        }
    }

}
